package com.inwavetech.os;

import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ScrollView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import com.inwavetech.os.entity.Os;
import com.inwavetech.os.entity.Product;
import com.inwavetech.os.util.field.BigDecimalField;
import com.inwavetech.os.util.field.EnumField;
import com.inwavetech.os.util.field.Field;
import com.inwavetech.os.util.field.FieldsCollection;
import com.inwavetech.os.util.field.SpinnerField;
import com.inwavetech.os.util.field.TextField;
import com.inwavetech.os.util.field.dynamicfields.DynamicFields;
import com.j256.ormlite.dao.Dao;

import java.sql.SQLException;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class OsDetailActivity extends BaseOsCheckActivity {

    private ViewGroup mainContainer;
    private Button submit;
    private OsValuesHandler osValuesHandler;
    private Dao productDao;
    private boolean editMode = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_os_detail);

        productDao = getHelper().getDao(Product.class);

        // the screen container
        mainContainer = (ViewGroup) findViewById(R.id.container);

        Os os = getUserOs();

        if (os == null) {
            backToMain();
            return;
        }

        osValuesHandler = new OsValuesHandler();

        if (!os.status.equals(Os.Status.ACTIVE)) {
            try {
                osValuesHandler.showNonEditable();
            } catch (Exception e) {
                logError(e);
            }
        }
    }

    @Override
    protected void onResume() {
        super.onResume();

        cancelEditMode();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        Os os = getOs();

        int resourceId = R.menu.main;

        if (os != null && !os.status.equals(Os.Status.FINISHED)) {
            resourceId = R.menu.os_detail;
        }

        getMenuInflater().inflate(resourceId, menu);

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        switch (item.getItemId()) {
            case R.id.action_logout:
                getSessionManager().logoutUser();

                break;

            case R.id.action_finish_os:
                toggleEdit();

                break;
        }

        return super.onOptionsItemSelected(item);
    }

    private void toggleEdit() {
        // create the submit bt if it does not exist
        if (submit == null) {
            submit = (Button) getLayoutInflater().inflate(R.layout.view_submit_bt, mainContainer, false);
            submit.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    try {
                        if (osValuesHandler.isValid()) {
                            osValuesHandler.updateEntity();
                            osValuesHandler.saveEntity();

                            startActivity(buildFinishIntent(os.id));
                        } else {
                            Toast.makeText(getApplicationContext(), getString(R.string.invalid_os_edit), Toast.LENGTH_LONG).show();
                        }
                    } catch (NoSuchFieldException e) {
                        logError(e);
                    } catch (IllegalAccessException e) {
                        logError(e);
                    } catch (SQLException e) {
                        logError(e);
                    }
                }
            });
        }

        if (!editMode) {
            editMode = true;

            try {
                osValuesHandler.showEditable();
            } catch (Exception e) {
                logError(e);
            }

            if (mainContainer.findViewById(R.id.submit) == null) {
                mainContainer.addView(submit);
            }

            // scroll
            View statusSpinner = findViewById(R.id.conclusion_info_container);
            ScrollView view = (ScrollView) findViewById(R.id.scrollcontainer);
            view.smoothScrollTo(0, statusSpinner.getBottom());
        } else {
            cancelEditMode();
        }
    }

    private void cancelEditMode() {
        if (editMode) {
            editMode = false;

            osValuesHandler.removeViews();
            mainContainer.removeView(submit);
        }
    }

    protected void logError(Exception e) {
        Log.e(OsDetailActivity.class.getName(), String.format("%s - %s", e.getClass().getName(), e.getMessage()));
    }

    private class SpinnerItem implements SpinnerField.AdapterItem {

        private final Enum anEnum;
        private final String statusString;

        public SpinnerItem(Enum anEnum, String string) {
            this.anEnum = anEnum;
            this.statusString = string;
        }

        @Override
        public String toString() {
            return statusString;
        }

        @Override
        public Object getValue() {
            return anEnum;
        }

    }

    abstract class EntityHandler {

        protected DynamicFields nonEditableFields;
        protected DynamicFields editableFields;

        public void showEditable() throws NoSuchFieldException, IllegalAccessException {
            editableFields.attach();
        }

        public void showNonEditable() throws NoSuchFieldException, IllegalAccessException {
            nonEditableFields.attach();
        }

        public void removeViews() {
            nonEditableFields.clearContainer();
            editableFields.clearContainer();
        }

        public void updateEntity() throws NoSuchFieldException, IllegalAccessException {
            editableFields.updateEntityValues();
        }

        public boolean isValid() {
            return editableFields.isValid();
        }

        protected Field configureSpinnerField(ViewGroup box, int resourceId, List<SpinnerField.AdapterItem> spinnerItems) {
            ArrayAdapter<SpinnerField.AdapterItem> adapter =
                    new ArrayAdapter<SpinnerField.AdapterItem>(getApplicationContext(),
                            R.layout.view_spinner_item, spinnerItems);

            Spinner spinner = (Spinner) box.findViewById(resourceId);
            spinner.setAdapter(adapter);

            return new SpinnerField(spinner);
        }

        protected Field configureDecimalView(ViewGroup box, int resourceId, DecimalFormat formatter) {
            return new BigDecimalField((TextView) box.findViewById(resourceId), formatter);
        }

        protected Field configureTextField(ViewGroup box, int resourceId) {
            return new TextField((TextView) box.findViewById(resourceId));
        }

        protected Field configureEnumField(ViewGroup box, int resourceId, Map<Enum, String> enumStringMap) {
            return new EnumField((TextView) box.findViewById(resourceId), enumStringMap);
        }

        protected List<SpinnerField.AdapterItem> buildSpinnerItems(Map<Enum, String> resourcesMap) {
            List<SpinnerField.AdapterItem> items = new ArrayList<SpinnerField.AdapterItem>();

            for (Enum anEnum: resourcesMap.keySet()) {
                items.add(new SpinnerItem(anEnum, resourcesMap.get(anEnum)));
            }

            return items;
        }

        protected Map<Enum, String> buildEnumStringMap(Map<Enum, Integer> resourcesMap) {
            Map<Enum, String> enumStringMap = new HashMap<Enum, String>();

            for (Enum anEnum: resourcesMap.keySet()) {
                enumStringMap.put(anEnum, getString(resourcesMap.get(anEnum)));
            }

            return enumStringMap;
        }
    }

    class OsValuesHandler extends EntityHandler {

        private final List<ProductViewHandler> productsHandlers;

        OsValuesHandler() {
            Os os = getOs();

            // configure the static views
            TextView titleText = (TextView) findViewById(R.id.title_id);
            TextView statusText = (TextView) findViewById(R.id.status);
            TextView registerNameText = (TextView) findViewById(R.id.register_accountable_name);
            TextView registerDateText = (TextView) findViewById(R.id.register_date);
            TextView activationNameText = (TextView) findViewById(R.id.activation_accountable_name);
            TextView activationDateText = (TextView) findViewById(R.id.activation_date);
            TextView clientNameText = (TextView) findViewById(R.id.client_name);
            TextView clientStoreText = (TextView) findViewById(R.id.client_store);
            TextView clientApplicantText = (TextView) findViewById(R.id.client_applicant);
            TextView clientContactText = (TextView) findViewById(R.id.client_contact);
            TextView clientTypeText = (TextView) findViewById(R.id.client_type);
            TextView clientAuthorizationText = (TextView) findViewById(R.id.client_authorization);
            TextView scheduleNameText = (TextView) findViewById(R.id.schedule_accountable_name);
            TextView scheduleDateText = (TextView) findViewById(R.id.schedule_date);
            TextView scheduleTimeText = (TextView) findViewById(R.id.schedule_time);

            titleText.setText(os.original_id);
            statusText.setText(getString(os.getStatusResource()));
            registerNameText.setText(os.opener);
            registerDateText.setText(os.getStringCreatedDate());
            activationNameText.setText(os.activated_user);
            activationDateText.setText(os.getStringActivatedDate());
            clientNameText.setText(os.client);
            clientStoreText.setText(os.store);
            clientApplicantText.setText(os.origin);
            clientContactText.setText(os.contact);
            clientTypeText.setText(getString(os.getServiceOrderTypeResource()));
            clientAuthorizationText.setText(os.authorization);
            scheduleNameText.setText(os.assignment);
            scheduleDateText.setText(os.getStringAssignmentDate());
            scheduleTimeText.setText(os.assignment_time);

            // configure the dynamic fields

            // get the container
            ViewGroup osDynamicViewsContainer = (ViewGroup) findViewById(R.id.conclusion_info_container);

            // map the closing status strings
            Map<Enum, String> closingStatusStringsMap = buildEnumStringMap(Os.closingStatusResources);

            // editable fields
            // inflate the box
            ViewGroup editableBox = (ViewGroup) getLayoutInflater().inflate(
                    R.layout.view_conclusion_edit, osDynamicViewsContainer, false);

            // configure the fields (entity/view relation)
            FieldsCollection editableConfig = new FieldsCollection();
            configureCommonDynamicFields(editableConfig, editableBox);

            // closing status
            editableConfig.put("closing_status", configureSpinnerField(editableBox,
                    R.id.conclusion_status, buildSpinnerItems(closingStatusStringsMap)));
            editableFields = new DynamicFields(os, editableConfig, osDynamicViewsContainer, editableBox);

            // non editable fields
            // inflate the box
            ViewGroup nonEditableBox = (ViewGroup) getLayoutInflater().inflate(
                    R.layout.view_conclusion_view, osDynamicViewsContainer, false);

            // configure the fields (entity/view relation)
            FieldsCollection nonEditableConfig = new FieldsCollection();
            configureCommonDynamicFields(nonEditableConfig, nonEditableBox);

            // closing status
            nonEditableConfig.put("closing_status", configureEnumField(nonEditableBox, R.id.conclusion_status, closingStatusStringsMap));
            nonEditableFields = new DynamicFields(os, nonEditableConfig, osDynamicViewsContainer, nonEditableBox);

            // configure the products handler
            // map the diagnosys strings
            Map<Enum, String> diagnosysStringsMap= buildEnumStringMap(Product.diagnosysResourcesMap);

            // get the container
            ViewGroup productsCollectionContainer = (ViewGroup) findViewById(R.id.products_container);

            productsHandlers = new ArrayList<ProductViewHandler>();

            for (Product product: os.products) {
                ViewGroup productContainer = (ViewGroup) getLayoutInflater().inflate(
                        R.layout.view_product_widget, productsCollectionContainer, false);
                productsCollectionContainer.addView(productContainer);

                productsHandlers.add(new ProductViewHandler(product, productContainer, diagnosysStringsMap));
            }
        }

        @Override
        public void showEditable() throws NoSuchFieldException, IllegalAccessException {
            super.showEditable();

            for (ProductViewHandler productViewHandler: productsHandlers) {
                productViewHandler.showEditable();
            }
        }

        @Override
        public void showNonEditable() throws NoSuchFieldException, IllegalAccessException {
            super.showNonEditable();

            for (ProductViewHandler productViewHandler: productsHandlers) {
                productViewHandler.showNonEditable();
            }
        }

        @Override
        public void removeViews() {
            super.removeViews();

            for (ProductViewHandler productViewHandler: productsHandlers) {
                productViewHandler.removeViews();
            }
        }

        @Override
        public void updateEntity() throws NoSuchFieldException, IllegalAccessException {
            super.updateEntity();

            for (ProductViewHandler productViewHandler: productsHandlers) {
                productViewHandler.updateEntity();
            }
        }

        public void saveEntity() throws SQLException {
            getOsDao().update(os);

            for (ProductViewHandler productViewHandler: productsHandlers) {
                productViewHandler.saveEntity();
            }
        }

        @Override
        public boolean isValid() {
            boolean isValid = super.isValid();

            for (ProductViewHandler productViewHandler: productsHandlers) {
                isValid = productViewHandler.isValid() && isValid;
            }

            return isValid;
        }

        private void configureCommonDynamicFields(FieldsCollection fields, ViewGroup box) {
            Locale localeBrasil = new Locale("pt", "BR");
            DecimalFormat formatter = new DecimalFormat("###,###,##0.00", new DecimalFormatSymbols(localeBrasil));

            fields.put("service_justification", configureTextField(box, R.id.conclusion_price_detail));
            fields.put("travel_justification", configureTextField(box, R.id.conclusion_travel_detail));
            fields.put("roundup", configureTextField(box, R.id.conclusion_roundup));
            fields.put("future_observation", configureTextField(box, R.id.conclusion_future_observation));
            fields.put("name_responsible", configureTextField(box, R.id.conclusion_accountable));

            fields.put("service_price", configureDecimalView(box, R.id.conclusion_price, formatter));
            fields.put("travel_price", configureDecimalView(box, R.id.conclusion_travel_price, formatter));

            TextView maintenance_observation = (TextView) box.findViewById(R.id.maintenance_observation);

            if (os.service_order_type.equals(Os.ServiceOrderType.INSTALL)) {
                box.removeView(maintenance_observation);
            } else {
                fields.put("maintenance_observation", new TextField(maintenance_observation));
            }
        }
    }

    class ProductViewHandler extends EntityHandler {
        private Product product;

        ProductViewHandler(Product product, ViewGroup productContainer, Map<Enum, String> diagnosysStringsMap) {
            Os os = getOs();
            this.product = product;

            // set the product id as a tag
            productContainer.setTag(String.valueOf(product.id));

            // static views
            TextView nameText = (TextView) productContainer.findViewById(R.id.product_name);
            TextView idText = (TextView) productContainer.findViewById(R.id.product_id);
            TextView documentCodeText = (TextView) productContainer.findViewById(R.id.product_document_code);
            TextView documentDateText = (TextView) productContainer.findViewById(R.id.product_document_date);
            TextView factoryInsurancyText = (TextView) productContainer.findViewById(R.id.product_factory_insurancy);
            TextView serviceInsuranceText = (TextView) productContainer.findViewById(R.id.product_service_insurancy);
            TextView solicitationText = (TextView) productContainer.findViewById(R.id.product_solicitation);
            TextView detailsText = (TextView) productContainer.findViewById(R.id.product_details);

            nameText.setText(product.name);
            idText.setText(product.identification);
            documentCodeText.setText(product.document_code);
            documentDateText.setText(product.getStringDocumentDate());
            factoryInsurancyText.setText(product.factory_warranty);
            serviceInsuranceText.setText(product.service_warranty);

            if (os.service_order_type.equals(Os.ServiceOrderType.INSTALL)) {
                solicitationText.setText(product.hired_services);
                detailsText.setText(product.setup_detail);
            } else {
                solicitationText.setText(product.maintenance_type);
                detailsText.setText(product.maintenance_detail);
            }

            // get the container
            ViewGroup dynamicFieldsContainer = (ViewGroup) productContainer.findViewById(R.id.product_diagnosys_container);

            // fields config
            FieldsCollection editableConfig = new FieldsCollection();
            FieldsCollection nonEditableConfig = new FieldsCollection();

            ViewGroup editableBox;
            ViewGroup nonEditableBox;

            if (os.service_order_type.equals(Os.ServiceOrderType.INSTALL)) {
                // view root for the install and dianosys fields
                editableBox = (ViewGroup) getLayoutInflater().inflate(R.layout.view_product_install_edit, dynamicFieldsContainer, false);
                nonEditableBox = (ViewGroup) getLayoutInflater().inflate(
                        R.layout.view_product_install_view, dynamicFieldsContainer, false);

                // configure the fields according to the
                editableConfig.put("installation_detail", configureTextField(editableBox, R.id.installation_detail));
                editableConfig.put("installation_observation", configureTextField(editableBox, R.id.installation_observation));
                nonEditableConfig.put("installation_detail", configureTextField(nonEditableBox, R.id.installation_detail));
                nonEditableConfig.put("installation_observation", configureTextField(nonEditableBox, R.id.installation_observation));
            } else {
                // view root for the install and dianosys fields
                editableBox = (ViewGroup) getLayoutInflater().inflate(R.layout.view_product_diagnosys_edit, dynamicFieldsContainer, false);
                nonEditableBox = (ViewGroup) getLayoutInflater().inflate(
                        R.layout.view_product_diagnosys_view, dynamicFieldsContainer, false);

                // configure the fields according to the
                editableConfig.put("diagnosys", configureSpinnerField(
                        editableBox, R.id.diagnosys, buildSpinnerItems(diagnosysStringsMap)));
                editableConfig.put("diagnosys_detail", configureTextField(editableBox, R.id.diagnosys_detail));
                nonEditableConfig.put("diagnosys", configureEnumField(nonEditableBox, R.id.diagnosys, diagnosysStringsMap));
                nonEditableConfig.put("diagnosys_detail", configureTextField(nonEditableBox, R.id.diagnosys_detail));
            }

            editableFields = new DynamicFields(product, editableConfig, dynamicFieldsContainer, editableBox);
            nonEditableFields = new DynamicFields(product, nonEditableConfig, dynamicFieldsContainer, nonEditableBox);
        }

        public void saveEntity() throws SQLException {
            productDao.update(product);
        }
    }
}
